#ifndef PARSER_H
#define PARSER_H

//==============================================================================
//
//                 Arguments - the argument parser class
//
//               Copyright (C) 2018  Dick van Oudheusden
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================

#include <string>
#include <vector>

namespace arg
{
  ///
  /// @class Argument
  ///
  /// @brief The base argument class.
  ///
  class Arguments;

  class Argument
  {
  public:
    ///
    /// Constructor for a switch argument
    ///
    /// @param  arguments       the arguments
    /// @param  optional        the optional indication
    /// @param  shortOption     the short option
    /// @param  longOption      the long option
    /// @param  help            the help text
    ///
    Argument(Arguments &arguments, bool optional, char shortOption, const std::string &longOption, const std::string &help);

    ///
    /// Constructor for a value argument
    ///
    /// @param  arguments       the arguments
    /// @param  optional        the optional indication
    /// @param  shortOption     the short option
    /// @param  longOption      the long option
    /// @param  valueInfo       the value info
    /// @param  helpInfo        the help info
    /// @param  initial         the initial value
    ///
    Argument(Arguments &arguments, bool optional, char shortOption, const std::string &longOption, const std::string &valueInfo, const std::string &helpInfo, const std::string &initial);

    ///
    /// Deconstructor
    ///
    virtual ~Argument();

  public:
    // Properties

    ///
    /// Return the optional indication
    ///
    /// @return the optional indication
    ///
    bool optional() const { return _optional; }

    ///
    /// Return the used indication
    ///
    /// @return the indication
    ///
    bool active() const { return _active; }

    ///
    /// Set the used indication
    ///
    /// @param the used indication
    ///
    void active(bool active) { _active = active; }

    ///
    /// Return the short option
    ///
    /// @return the short option
    ///
    char shortOption() const { return _shortOption; }

    ///
    /// Return the long option
    ///
    /// @return the long option
    ///
    const std::string &longOption() const { return _longOption; }

    ///
    /// Return the long option info
    ///
    /// @return the long option info
    ///
    const std::string &valueInfo() const { return _valueInfo; }

    ///
    /// Return the indication if the options needs a value
    ///
    /// @return the indication if the options needs a value
    ///
    virtual bool withValue() const { return _withValue; }

    ///
    /// Set the value for the argument
    ///
    /// @param the value
    ///
    virtual void value(const std::string &value) { _value = value; }

    ///
    /// Get the value of the argument
    ///
    /// @return the value
    ///
    virtual std::string value() const { return _value; }

    ///
    /// Print the argument help text
    ///
    /// @param column      the column number for the help text
    ///
    void print(int column) const;

    ///
    /// Print an argument help text
    ///
    /// @param shortOption   the short option character
    /// @param longOption    the long option
    /// @param valueInfo     the value info text
    /// @param helpInfo      the help text
    /// @param column        the column for the help text
    ///
    static void print(char shortOption, const std::string &longOption, const std::string &valueInfo, const std::string &helpInfo, int column);

    ///
    /// Return the option width
    ///
    /// @return the option width
    ///
    virtual int optionWidth() const;

  private:
    bool           _optional;
    bool           _withValue;
    bool           _active;
    char           _shortOption;
    std::string    _longOption;
    std::string    _valueInfo;
    std::string    _value;
    std::string    _helpInfo;

    // Disable copy constructors
    Argument(const Argument &);
    Argument& operator=(const Argument &);
  };

  ///
  /// @class Arguments
  ///
  /// @brief The arguments parser class.
  ///
  class Arguments
  {
  public:
    ///
    /// Constructor
    ///
    /// @param usage         the usage text
    /// @param version       the version of the program
    /// @param help          the program help text
    ///
    Arguments(const std::string &usage, const std::string &version, const std::string &help);

    ///
    /// Deconstructor
    ///
    virtual ~Arguments();

    ///
    /// Add an argument to the arguments parser
    ///
    /// @param argument   an argument
    ///
    void add(Argument &argument);

    ///
    /// Parse the arguments
    ///
    /// @param argc        the number of command line arguments
    /// @param argv        the command line arguments
    /// @param unprocessed the unprocessed command line arguments
    ///
    /// @return success
    ///
    bool parse(int argc, char *argv[], std::vector<std::string> &unprocessed);

    ///
    /// Print the help
    ///
    void printHelp() const;

  private:
    bool parseShortOption(int argc, char *argv[], int &arg);
    bool parseLongOption(int argc, char *argv[], int arg);
    bool checkRequiredArgument(Argument *argument);

    std::string             _usage;
    std::string             _version;
    std::string             _help;

    std::vector<Argument *> _arguments;

    // Disable copy constructors
    Arguments(const Arguments &);
    Arguments& operator=(const Arguments &);
  };
}
#endif

